package queue

import (
	"container/list"
	"sync"
	"sync/atomic"
	"time"
)

type (
	// Task 定义定时任务的数据结构
	Task struct {
		id      int64         // 任务唯一ID
		job     func()        // 任务函数
		delay   int64         // 定时任务执行时间戳
		cancel  bool          // 用于标记任务已被取消(惰性删除)
		period  time.Duration // 重复执行间隔(0表示不重复)
		repeats int           // 重复执行次数(-1表示一直重复；0表示不重复；>0表示指定次数)
	}

	// DelayQueue 定义延时队列的数据结构
	DelayQueue struct {
		queue        *list.List
		tickDuration time.Duration // 延时队列的嘀嗒间隔时间
		taskMap      sync.Map      // 任务id映射，用于取消任务
		taskChan     chan *Task    // 任务队列
		stopChan     chan struct{} // 停止通道
		idGenerator  atomic.Int64  // 任务id生成器
		isRunning    atomic.Bool   // 运行状态原子标记

		waitGroup sync.WaitGroup
	}
)

// NewDelayQueue 新建一个延时队列
func NewDelayQueue(tickDuration time.Duration, chanSize ...int) *DelayQueue {
	if tickDuration <= 0 {
		panic("NewDelayQueue: tick duration must be greater than zero")
	}

	queue := &DelayQueue{
		queue:        list.New(),
		tickDuration: tickDuration,
		stopChan:     make(chan struct{}),
	}

	if len(chanSize) > 0 && chanSize[0] > 0 {
		queue.taskChan = make(chan *Task, chanSize[0])
	} else {
		queue.taskChan = make(chan *Task, 1024)
	}

	return queue
}

// Start 启动延时队列
func (own *DelayQueue) Start() {
	// 检查延时队列是否已启动
	if own.isRunning.Swap(true) {
		return
	}

	// 创建延时队列驱动计时器
	ticker := time.NewTicker(own.tickDuration)
	own.waitGroup.Add(1)

	// 异步允许延时队列
	go func() {
		defer own.waitGroup.Done()
		defer ticker.Stop()

		for {
			select {
			case task := <-own.taskChan:
				own.addTask(task)
			case <-ticker.C:
				own.tick()
			case <-own.stopChan:
				return
			}
		}
	}()
}

// Stop 停止延时队列
func (own *DelayQueue) Stop() {
	// 检查队列是否已停止
	if !own.isRunning.Load() {
		return
	}

	// 停止队列运行
	own.isRunning.Store(false)
	close(own.stopChan)
	own.waitGroup.Wait()
	close(own.taskChan)
}

// AddTask 添加定时(循环)任务
//
// 注意：重复执行任务的间隔时间 period 需要大于延时队列的最小嘀嗒时间；repeats=-1 表示一直定时执行任务
func (own *DelayQueue) AddTask(delay time.Time, job func(), period time.Duration, repeats ...int) int64 {
	// 检查延时队列是否已停止
	if !own.isRunning.Load() {
		return 0
	}

	task := &Task{
		id:      own.idGenerator.Add(1),
		job:     job,
		delay:   delay.UnixNano(),
		repeats: 1,
	}

	if period > 0 {
		task.period = period
	}

	if task.period > 0 && len(repeats) > 0 {
		if repeats[0] < 0 {
			task.repeats = repeats[0]
		} else {
			task.repeats += repeats[0]
		}
	}

	// 添加任务到任务队列
	own.taskChan <- task
	return task.id
}

// CancelTask 取消任务
func (own *DelayQueue) CancelTask(tid int64, fn func()) bool {
	if value, loaded := own.taskMap.LoadAndDelete(tid); loaded {
		value.(*Task).cancel = true
		if fn != nil {
			fn()
		}

		return true
	}

	return false
}

// addTask 添加任务到延时队列
func (own *DelayQueue) addTask(task *Task) {
	// 只要 task.repeats == 0，则表明任务执行不需要在执行
	if task.repeats == 0 {
		return
	}

	// 定时时间不足一个嘀嗒时间，则立即执行
	if task.delay <= time.Now().UnixNano() {
		own.callback(task)
		return
	}

	// 加入队列
	own.queue.PushBack(task)
	// 保存到任务映射
	own.taskMap.Store(task.id, task)
}

// tick 周期性处理延时队列
func (own *DelayQueue) tick() {
	now := time.Now().UnixNano()
	var next *list.Element
	for e := own.queue.Front(); e != nil; e = next {
		task := e.Value.(*Task)
		next = e.Next()

		// 任务取消或重复执行次数结束
		if task.cancel || task.repeats == 0 {
			own.taskMap.Delete(task.id)
			own.queue.Remove(e)
			continue
		}

		// 任务时间未到
		if task.delay > now {
			continue
		}

		// 任务时间到，执行回调
		own.callback(task)

		// 若 task.repeats>0，则重复次数减1
		if task.repeats > 0 {
			task.repeats--
		}

		// 检查任务是否可继续执行
		if task.period > 0 && task.repeats != 0 {
			task.delay = time.Now().Add(task.period).UnixNano()
			if own.isRunning.Load() {
				own.taskChan <- task
			}
		} else {
			own.taskMap.Delete(task.id)
			own.queue.Remove(e)
		}
	}
}

func (own *DelayQueue) callback(task *Task) {
	own.waitGroup.Add(1)
	go func() {
		task.job()
		own.waitGroup.Done()
	}()
}
